import MessageHandlerManager from '@/proto/message/MessageHandlerManager';

class WebSocketHandler {
  private static _instance: WebSocketHandler = new WebSocketHandler();
  private _ws: WebSocket;

  private heartBeatInterval: number ;
  private reConnectTimeout: number;
  private reConnectAttempts = 0;
  private reConnectMaxAttempts = 5;

  private constructor() {
    this.connect();
  }

  public static get instance(): WebSocketHandler {
    return this._instance;
  }

  public static sendWs(data: Uint8Array): void {
    if (WebSocketHandler.instance._ws && WebSocketHandler.instance._ws.readyState === WebSocket.OPEN) {
      WebSocketHandler.instance._ws.send(data);
    } else {
      console.error('WebSocket is not connected.');
    }
  }

  private connect(): void {

    if (this._ws != null){
      return;
    }
    const socketUrl = import.meta.env.VITE_AURORA_WEBSOCKET_URL as string;
    console.debug('Attempting to connect WebSocket', socketUrl);

    this._ws = new WebSocket(socketUrl);

    this._ws.onopen = () => {
      console.debug('WebSocket connected.');
      this.startHeartbeat();
      this.reConnectAttempts = 0;
    };

    this._ws.onmessage = (event) => {
      event.data.arrayBuffer().then((buf: ArrayBuffer) => {
        const msg = MessageHandlerManager.decode(buf);
        window.dispatchEvent(new CustomEvent('onmessageWS', { detail: { data: msg } }));
      });
    };

    this._ws.onclose = () => {
      console.debug('WebSocket disconnected.');
      this.stopHeartbeat();
      this.tryReconnect();
    };

    this._ws.onerror = (err) => {
      console.error('WebSocket encountered an error:', err);
      this._ws?.close();
    };
  }

  private tryReconnect(): void {
    if (this.reConnectAttempts < this.reConnectMaxAttempts) {
      this.reConnectTimeout = setTimeout(() => {
        console.debug(`Reconnecting attempt ${this.reConnectAttempts + 1}`);
        this.reConnectAttempts++;
        this.connect();
      }, 3000);
    } else {
      console.error('Maximum WebSocket reconnection attempts reached.');
    }
  }

  private startHeartbeat(): void {
    this.heartBeatInterval = setInterval(() => {
      this._ws?.send(MessageHandlerManager.encode({}, 200));
    }, 8000);
  }

  private stopHeartbeat(): void {
    if (this.heartBeatInterval) {
      clearInterval(this.heartBeatInterval);
      this.heartBeatInterval = null;
    }
  }
}

export default WebSocketHandler;
